#include <FileDescriptor.h>
#include <Heap.h>
#include <ProcessManager.h>
#include <Filesystem.h>

FileDescriptor::FileDescriptor(char *filename, ModeType mode, Flags flags,
                               s32int id) :
    offset(0), mode(mode), flags(flags)
{
  // Initialise the resource and functions.
  resource = new Resource((Callable*)this);
  resource->add((Resource::Call0)&FileDescriptor::isValid,         FileDescriptor_IF::isValid);
  resource->add((Resource::Call0)&FileDescriptor::close,           FileDescriptor_IF::close);
  resource->add((Resource::Call2)&FileDescriptor::seek,            FileDescriptor_IF::seek);
  resource->add((Resource::Call2)&FileDescriptor::read,            FileDescriptor_IF::read);
  resource->add((Resource::Call2)&FileDescriptor::write,           FileDescriptor_IF::write);
  resource->add((Resource::Call0)&FileDescriptor::getFileResource, FileDescriptor_IF::getFile);
  resource->add((Resource::Call0)&FileDescriptor::getId,           FileDescriptor_IF::getId);
  
  file = filesystem->traverse(filename);
  if (file)
  {
    if (id == -1)
    {
      fd = processManager->getProcess()->getNextFD();
    }
    else
    {
      fd = id;
    }
    if (!file->open(this))
      file = NULL;
  }
}

FileDescriptor::~FileDescriptor()
{
  if (file)
  {
    file->close(this);
  }
  delete resource;
}

bool FileDescriptor::isValid()
{
  return (file != NULL);
}

void FileDescriptor::close()
{
  if (file)
  {
    file->close(this);
  }
}

u32int FileDescriptor::seek(u32int off, SeekType type)
{
  // So that we don't drown the user in useless debug messages don't dump messages for the first
  // three fd's (stdin,stdout,stderr)
  if (fd > 2)
  {
    DEBUG_MSG("seek(" << dec << fd << ", " << hex << off << ", " << type << ").");
  }
  if (!file)
  {
    return -1;
  }
  switch(type)
  {
    case Set:
      if (off > file->length())
      {
        offset = file->length();
      }
      else
      {
        offset = off;
      }
      break;
	
    case Curr:
      if (off+offset > file->length())
      {
        offset = file->length();
      }
      else
      {
        offset += off;
      }
      break;
	
    case End:
      if (off > file->length())
      {
        offset = 0;
      }
      else
      {
        offset = file->length() - off;
      }
  }
  return offset;
}

u32int FileDescriptor::read(u32int length, u32int bufferResource)
{
  // So that we don't drown the user in useless debug messages don't dump messages for the first
  // three fd's (stdin,stdout,stderr)
  if (fd > 2)
  {
    DEBUG_MSG("read(" << hex << fd << ", " << hex << length << ", " << (u32int)bufferResource << ").");
  }
  if (!file || mode == WriteOnly)
  {
    return 0;
  }

  if(offset >= file->length())
  {
    return 0;
  }
  
  u32int sz = 0xFFFFFFFF;
  while (sz == 0xFFFFFFFF) // file->read will return -1 if the read is not
                           // instantaneous (i.e. driver has to wait for an
                           // interrupt and doesn't want us clogging up it's
                           // runtime.
  sz = file->read(offset, length, bufferResource);
  offset += sz;
  return sz;
}

u32int FileDescriptor::write(u32int length, u32int bufferResource)
{
  // So that we don't drown the user in useless debug messages don't dump messages for the first
  // three fd's (stdin,stdout,stderr)
  if (fd > 2)
  {
    DEBUG_MSG("write(" << hex << fd << ", " << hex << length << ", " << (u32int)bufferResource << ").");
  }
  if (!file || mode == ReadOnly)
  {
    return 0;
  }
  u32int sz = 0xFFFFFFFF;
  while (sz == 0xFFFFFFFF) // file->write will return -1 if the read is not
                           // instantaneous (i.e. driver has to wait for an
                           // interrupt and doesn't want us clogging up it's
                           // runtime.
    sz = file->write(offset, length, bufferResource);
  offset += sz;
  return sz;
}

u32int FileDescriptor::getId()
{
  return fd;
}

FileDescriptor *FileDescriptor::clone()
{
  u8int *n = (u8int*)memoryManager.malloc(sizeof(FileDescriptor));
  memcpy(n, (u8int*)this, sizeof(FileDescriptor));
  FileDescriptor *myfd = (FileDescriptor*)n;
  myfd->resource = new Resource((Callable*)myfd);
  myfd->resource->add((Resource::Call0)&FileDescriptor::isValid,         FileDescriptor_IF::isValid);
  myfd->resource->add((Resource::Call0)&FileDescriptor::close,           FileDescriptor_IF::close);
  myfd->resource->add((Resource::Call2)&FileDescriptor::seek,            FileDescriptor_IF::seek);
  myfd->resource->add((Resource::Call2)&FileDescriptor::read,            FileDescriptor_IF::read);
  myfd->resource->add((Resource::Call2)&FileDescriptor::write,           FileDescriptor_IF::write);
  myfd->resource->add((Resource::Call0)&FileDescriptor::getFileResource, FileDescriptor_IF::getFile);
  myfd->resource->add((Resource::Call0)&FileDescriptor::getId,           FileDescriptor_IF::getId);
  return myfd;
}

File *FileDescriptor::getFile()
{
  return file;
}

u32int FileDescriptor::getFileResource()
{
  return file->resource->getId();
}
